﻿package {	import flash.display.MovieClip;
	import flash.events.Event;
	import flash.events.TimerEvent;
	import flash.geom.Point;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import flash.utils.Timer;    
	/**	 * @author chrisrauh	 */	public class Main extends MovieClip {		public static const FONT : String = "Helvetica Neue";		var w : Number = 800;		var h : Number = 600;		var pa : Array;
		var va : Object;		var num : Number;		var timer : Timer;		var stepCounter : StepCounter;		var data : Array;		var dataTimer : Timer;		var dataTimerStep : Number;		public function Main() {						trace("Start");						var dataTimerStepParam : String = root.loaderInfo.parameters.timerStep;			dataTimerStep = Number(dataTimerStepParam);			
			var dataPath : String = root.loaderInfo.parameters.dataPath;			if (!dataPath) {				dataPath = "data/chrisrauh_twitter_replies.csv";			}						var urlRequest : URLRequest = new URLRequest(dataPath);			var urlLoader : URLLoader = new URLLoader();			urlLoader.addEventListener(Event.COMPLETE, init);			urlLoader.load(urlRequest);			
		}
		public function init(event : Event) : void {			
			if (stage.width > 0) {
				w = stage.width;
			}
			if (stage.height > 0) {
				h = stage.height;
			}
			
			var csvData : String = (event.currentTarget as URLLoader).data as String;
			pa = parseData(csvData);
			num = pa.length;											initParticles();
						stepCounter = new StepCounter(data.length);			stepCounter.x = 5;			stepCounter.y = 5;			addChild(stepCounter);						va = new Object();			if (dataTimerStep > 0) {								dataTimer = new Timer(dataTimerStep);				dataTimer.addEventListener(TimerEvent.TIMER, stepData);				dataTimer.start();							} else {								while (data.length > 0) {					stepData();				}							}						timer = new Timer(20);			timer.addEventListener(TimerEvent.TIMER, update);			timer.start();		}
		public function parseData(dataString : String) : Array {
        	
			var i : int, j : int;
        	
			data = dataString.split("\n");
			for (i = 0;i < data.length; i++) {
				data[i] = String(data[i]).split(",");
			}
        	
			var itemLabels : Array = data.shift();			var numItems : Number = itemLabels.length;
        	
			var countMatrix : Array = new Array(numItems);
			for (i = 0;i < numItems; i++) {
				countMatrix[i] = new Array(numItems);
				for (j = 0;j < numItems; j++) {
					countMatrix[i][j] = 0;
				}
			}
        	
			var result : Array = new Array(numItems);
			for (i = 0;i < numItems; i++) {
				result[i] = {id: i, label: itemLabels[i], force:countMatrix[i]};			}						return result;
		}		protected function stepData(event : TimerEvent = null) : void {						if (data.length > 0) {								var dataItem : Array = data.shift();			
				var i1 : Number = -1;
				var i2 : Number = -1;
				for (var j : int = 0;j < num; j++) {
					if (dataItem[j] == 1) {
						if (i1 == -1) {
							i1 = j;
						} else {
							i2 = j;
						}
					}				}								// Increment the force between the particles.				pa[i1].force[i2] = pa[i1].force[i2] + 1;
				pa[i2].force[i1] = pa[i2].force[i1] + 1;								// Check if a connection exists				var c1 : ParticleConnection = va[i1 + "_" + i2]; 				if (c1) {					c1.strength = pa[i1].force[i2] + pa[i2].force[i1];				} else {					c1 = new ParticleConnection(pa[i1].p, pa[i2].p, pa[i1].force[i2] + pa[i2].force[i1]);					va[i1 + "_" + i2] = c1;					addChildAt(c1, 0);				}								stepCounter.increment();							} else {								if (dataTimer) {					dataTimer.stop();					dataTimer.removeEventListener(TimerEvent.TIMER, stepData);  				}			}		}
		public function initParticles() : void {			for (var i : int = 0;i < num; i++) {				var p : Particle = new LabelParticle(pa[i].label);				p.index = i;				p.links = pa[i].force as Array;				addChild(p);				p.x = Math.random() * w / 2 + w / 4;				p.y = Math.random() * h / 2 + h / 4;				pa[i].p = p;			}		}
		public function update(event : TimerEvent) : void {			//trace(".");						// Total kinetic energy			var kt : Number = 0; 			var timestep : Number = 1;			var damping : Number = 0.6;						for (var i : int = 0;i < num; i++) {								//trace("i = " + i);				var p : Particle = pa[i].p;				var r : Point;				var j : int;								// Net force, running sum of total force on this particular node				// starts at zero				var fx : Number = 0;				var fy : Number = 0;								for (j = 0;j < num; j++) {					if (j != i) {						r = CoulumbRepulsion(p, pa[j].p);						fx += r.x;						fy += r.y;					}				}								for (j = 0;j < num; j++) {					if (j != i) {						r = HookeAttraction(p, pa[j].p);						fx += r.x;						fy += r.y;					}				}								p.vx = ( p.vx + timestep * fx ) * damping;				p.vy = ( p.vy + timestep * fy ) * damping;								//trace("vx = " + p.vx + " | vy = " + p.vy);				kt += p.m * fx * fx + p.m * fy * fy;			}							for (i = 0;i < num; i++) {				p = pa[i].p;				p.x += p.vx;				if (p.x < 0) p.x = 0;				if (p.x > w) p.x = w;				p.y += p.vy;				if (p.y < 0) p.y = 0;				if (p.y > h) p.y = h;			}
            
			for (var s : String in va) {
				ParticleConnection(va[s]).update();			}
		}		public function CoulumbRepulsion(a : Particle, b : Particle) : Point {			var k : Number = -100;			var dx : Number = b.x - a.x;			var dy : Number = b.y - a.y;			//trace("dx = " + dx + " | dy = " + dy);			var r2 : Number = dx * dx + dy * dy;			//trace("r2 = " + r2);			var f : Number = (k * a.q * b.q) / r2;			if (f > 100) f = 100;			//trace("f = " + f);			var t : Number = Math.atan2(dy, dx);			//trace("t = "+ t);			var fx : Number = f * Math.cos(t);			var fy : Number = f * Math.sin(t);			//trace("fx = " + fx + " | fy = " + fy);			return new Point(fx, fy);		}		public function HookeAttraction(a : Particle, b : Particle) : Point {			var k : Number = 0.01;			var dx : Number = b.x - a.x;			var dy : Number = b.y - a.y;			//trace("dx = " + dx + " | dy = " + dy);			var d : Number = Math.sqrt(dx * dx + dy * dy);			//trace("r2 = " + r2);			var link : Number = a.links[b.index];			var f : Number = link * k * d;			//trace("f = " + f);			var t : Number = Math.atan2(dy, dx);			//trace("t = "+ t);			var fx : Number = f * Math.cos(t);			var fy : Number = f * Math.sin(t);			//trace("fx = " + fx + " | fy = " + fy);			return new Point(fx, fy);		}	}}